#ifndef CHROMOSOME_H_
#define CHROMOSOME_H_

#include "Gene.h"

#include <vector>

template <typename T>
class Chromosome
{
public:
	Chromosome(unsigned int num_genes, unsigned int num_pieces)
	{
		Gene<T> * temp;

		for (unsigned int i = 0; i < num_genes; i++)
		{
			temp = new Gene<T> (num_pieces);
			genes.push_back(temp);
		}
		
		this->num_genes = num_genes;
		this->num_pieces = num_pieces;
	}

	virtual ~Chromosome()
	{
		//for (typename std::vector<Gene<T>* >::iterator i = genes.begin(); i != genes.end(); i++)
		//{
		//	delete *i;
		//}
	}
	
	Chromosome<T> &operator= (const Chromosome<T> &rhs)
	{
		if (this == &rhs)
			return *this;
		
		num_genes  = rhs.get_num_genes();
		num_pieces = rhs.get_num_pieces();
		fitness    = rhs.get_fitness();
		
		for (unsigned int i = 0; i < num_genes; i++)
		{
			*(genes[i]) = rhs[i];
		}
		
		return *this;
	}

	// Operator granting (write) access to individual genes from array
	Gene<T> &operator[] (unsigned int offset)
	{
		return *(genes[offset]);
	}
	
	// Const (read only) version of the above
	const Gene<T> &operator[] (unsigned int offset) const
	{
		return *(genes[offset]);
	}

	// Breed two Chromsomes and return result
	Chromosome<T> operator^ (Chromosome<T> &rhs)
	{
		if (genes.size() != rhs.get_num_genes())
		{
			return rhs;
		}
		
		Chromosome<T> result(rhs.get_num_genes(), rhs.get_num_pieces());

		for (unsigned int i = 0; i < rhs.get_num_genes(); i += 2)
		{
			result[i] = *(genes[i]);
		}

		for (unsigned int i = 1; i < rhs.get_num_genes(); i += 2)
		{
			result[i] = rhs[i];
		}
		
		return result;
	}
	
	bool operator< (Chromosome<T> &rhs)
	{
		return (get_fitness() < rhs.get_fitness());
	}
	
	bool operator> (Chromosome<T> &rhs)
	{
		return !(*this < rhs);
	}
	
	static bool cmp(Chromosome<T> *lhs, Chromosome<T> *rhs)
	{
		return (*lhs < *rhs);
	}
	
	double get_fitness() const
	{
		return fitness;
	}

	void set_fitness(double in_fit)
	{
		fitness = in_fit;
	}

	unsigned int get_num_genes() const
	{
		return genes.size();
	}

	unsigned int get_num_pieces() const
	{
		return genes[0]->get_num_pieces();
	}

	// Call randomise_data on each gene
	void randomise_all_genes(T lower_bound, T upper_bound)
	{
		// Swap lower bound and upper bound if lower is greater than upper
		if (lower_bound > upper_bound)
		{
			T temp = upper_bound;
			upper_bound = lower_bound;
			lower_bound = temp;
		}
		
		for (unsigned int i = 0; i < genes.size(); i++)
		{
			genes[i]->randomise_data(lower_bound, upper_bound);
		}
	}

private:
	std::vector<Gene<T>* > genes;
	double fitness;
	
	unsigned int num_genes, num_pieces;
};

#endif /*CHROMOSOME_H_*/
